package dk.aiszone.lang.sql;

/**
 * The <code>SQLAssignableField</code> is when a field is assigned a value.<br>
 * Either as part of a constrait or an insert or update statement.
 * 
 * @author Ais
 */
public class SQLAssignableField extends AbstractSQLObject {

    private String field;
    private final String id;
    private ThreadLocal<ObjectHolder> threadLocalValue = new ThreadLocal<ObjectHolder>();
    private Object value;
    private boolean valueAssignedOnConstruction;

    /**
     * Constructor for the <code>SQLAssignableField</code> object.
     * 
     * @param field
     *            The field to match
     * @param value
     *            The value to match.<br>
     *            Depending on the type of the value, the following will happen:<br>
     *            If the value is an instance of <code>String.class</code> the <code>SQLEquals</code> method will automatically<br>
     *            wrap the value in single quotes.<br>
     *            If the <code>class</code> is either a <code>boolean</code> or <code>Number</code><br>
     *            the value will remain unwrapped.<br>
     *            If the <code>class</code> is a <code>SQLObject</code> it's prepare will be added to the <code>SQLResult</code>.<br>
     *            If <code>null</code> then <b>null</b> will be written.
     */
    public SQLAssignableField(SQLField field, Object value) {
        this.id = null;
        this.field = field.getFormattedName();
        this.value = value;
        this.valueAssignedOnConstruction = true;
    }

    /**
     * Constructor for the <code>SQLAssignableField</code> object.
     * 
     * @param id
     *            The id of the <code>SQLEquals</code>.<br>
     *            This Id can be used later on to set the value of the field.
     * @param field
     *            The field to match
     */
    public SQLAssignableField(String id, SQLField field) {
        this.id = id;
        this.field = field.getFormattedName();
        this.valueAssignedOnConstruction = false;
    }

    /**
     * Retrieve the field name of the <code>SQLAssignableField</code>
     * 
     * @return The contained field name.
     */
    public String getField() {
        return field;
    }

    /**
     * Retrieve the id assigned to the <code>SQLAssignableField</code>.
     * 
     * @return The assigned id.
     */
    public String getId() {
        return id;
    }

    /**
     * Retrieve the assigned value of the <code>SQLAssignableField</code>
     * 
     * @return The assigned value.
     */
    public Object getValue() {
        if (valueAssignedOnConstruction) {
            return value;
        }

        ObjectHolder oh = threadLocalValue.get();
        if (oh == null) {
            return null;
        }
        return oh.value;
    }

    /**
     * Retrieve whether the <code>SQLAssignableField</code> is assignable.
     * 
     * @return <code>true</code> if the field is assignable - otherwise <code>false</code>.
     */
    public boolean isAssignable() {
        return !valueAssignedOnConstruction;
    }

    /**
     * Retrieve whether the <code>SQLAssignableField</code> has been assigned a value.
     * 
     * @return <code>true</code> if assigned - otherwise <code>false</code>.
     */
    public boolean isAssigned() {
        return valueAssignedOnConstruction || (getValue() != null);
    }

    /**
     * Set the value of the the <code>SQLAssignableField</code>.
     * 
     * @param value
     *            The value to assign.
     */
    public void setValue(Object value) {
        if (valueAssignedOnConstruction) {
            throw new IllegalStateException("Value not assignable, if already assigned during construction of the object.");
        }
        threadLocalValue.set(new ObjectHolder(value));
    }

    private class ObjectHolder {

        /* pp */Object value;

        /* pp */ObjectHolder(Object value) {
            this.value = value;
        }

        @Override
        public String toString() {
            return "Value = " + value;
        }
    }
}